package com.lucanjun.cn.springboot.common.utils;

import com.lucanjun.cn.springboot.common.exception.BusinessException;
import com.lucanjun.cn.springboot.common.result.HttpResultCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;

/**
 * @author lucanjun
 * @version 1.0.0
 * @title RSAUtil
 * @create 2024-12-19 16:55:19
 * @description RSA非对称加密工具类
 */
@Slf4j
@Component
public class RSAUtil {

    /**
     * 非对称加密算法：
     * 非对称加密是一种加密算法，它使用一对相关的密钥（公钥和私钥）来进行加密和解密操作。
     * 这种加密方法也被称为公钥加密，因为公钥可以公开分享给其他人，而私钥必须保密。
     * 非对称加密的关键特点：
     * 非对称加密的关键特点是公钥和私钥的不对称性。
     * 公钥用于加密数据，私钥用于解密数据。由于私钥是保密的，
     * 只有私钥的持有者能够解密数据，这样可以确保数据的机密性。
     * 公钥则可以公开分享给其他人，用于加密数据，以便发送给私钥的持有者。
     */

    /**
     * 非对称加密算法的工作原理如下：
     * 生成密钥对：首先，使用非对称加密算法（如RSA或ECC）生成一对密钥，包括公钥和私钥。
     * 公钥加密：公钥用于加密数据。发送方使用接收方的公钥对数据进行加密，并将加密后的数据发送给接收方。
     * 私钥解密：接收方使用自己的私钥对收到的加密数据进行解密，以恢复原始的明文数据。
     */

    /**
     * 非对称加密的应用场景包括：
     * 加密通信：可以使用接收方的公钥对数据进行加密，确保只有接收方的私钥持有者能够解密数据，从而实现安全的通信。
     * 数字签名：发送方可以使用自己的私钥对数据进行签名，接收方可以使用发送方的公钥验证签名的有效性，确保数据的完整性和认证性。
     * 密钥交换：可以使用非对称加密算法安全地交换对称密钥，从而在后续的通信中使用更高效的对称加密算法进行加密。
     */

    /**
     * 1、概述
     * 与对称加密算法不同，非对称加密算法需要两个密钥：公开密钥(publickey) 和私有密钥(privatekey)。
     * 公开密钥和私有密钥是一对，如果用公开密钥加密需要私有密钥解密，反之用私有密钥加密需要公开密钥解密，因为加密和解密使用的是两个不同的密钥，所以这种算法叫作非对称加密算法。
     * <p>
     * 特点：
     * 加密和解密使用不同的密钥。
     * 如果使用私钥加密, 只能使用公钥解密。
     * 如果使用公钥加密, 只能使用私钥解密。
     * 安全级别高，处理数据的速度较慢。
     * <p>
     * 注意：
     * 理论上私钥是可以推导出公钥的，公钥无法推导出私钥。
     * <p>
     * 私钥中获取公钥测试地址：http://tool.chacuo.net/cryptgetpubkey
     */

//    //RSA加密算法
//    private final static String RSA_ALGORITHM = "RSA";
//    //密钥长度
//    private final static int KEY_LENGTH = 2048;
//    //签名算法
//    private final static String SIGNATURE_ALGORITHM = "SHA256withRSA";
//    //公钥
//    private final static String RSA_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxuOe5K6KNI8Eb/+CdvHcOvJHKs4I3MRZApeM9xIM5C+wJvYqJlV1d2nwPox/I8E5p1zHWLBMJo4Dzhhi78OYYrIxCrF6yukNDTjKx5zRaxKmzavTmV1xL0ssRev4w/gCWU9YraLm17tbCMVqcP64lqt3OodXghpRn3riWMEnGHV1NLg45N6C0ZvgE8Qf3IrmnEGfTtU1eKD397WP9RAb/dzMZM9nf8TH5zM/MdfA2+jY2KsCFkkUG0oWPPWx4t/GwxhO4U/V+TDHu7XFAsyEV9V9gPMifSrXeWzlAVeFemd62/79Kl/2zFVdNXiYbIRJcDd8a77PKfqPeNAY8XM1NQIDAQAB";
//    //私钥
//    private final static String RSA_PRIVATE_KEY = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDG457kroo0jwRv/4J28dw68kcqzgjcxFkCl4z3EgzkL7Am9iomVXV3afA+jH8jwTmnXMdYsEwmjgPOGGLvw5hisjEKsXrK6Q0NOMrHnNFrEqbNq9OZXXEvSyxF6/jD+AJZT1itoubXu1sIxWpw/riWq3c6h1eCGlGfeuJYwScYdXU0uDjk3oLRm+ATxB/ciuacQZ9O1TV4oPf3tY/1EBv93Mxkz2d/xMfnMz8x18Db6NjYqwIWSRQbShY89bHi38bDGE7hT9X5MMe7tcUCzIRX1X2A8yJ9Ktd5bOUBV4V6Z3rb/v0qX/bMVV01eJhshElwN3xrvs8p+o940BjxczU1AgMBAAECggEABoHuHKy6qEDsXpRtUA0BLWCTWYrc7rRHS/a/eFoRLT+DiP18Sugi7CGoPbOvzptaPT1OPHr66Da0rdVNxMCRR4bDD1wtQ18Ku/MEF4dBsY+Dq65tQahgCrHFT2hgKbFUtQK2nmp6bnaajO1Ar//K9GBvNJgMZ0e111JYU5pw+AjZAold4lZoWzV0vnnHNLLFvu8Qg/J8k1ciMecyh3ebGttygMu5mEKS/3lRNOZuMyQe9QS+bCkGBi9QHeTx/QrLtqW3enUZGGWYC8pnvcgJYBKRL/f35kDlR2YuIFj4V+WNwhya/vEusOF4v0GeaTiInavQYFhbAq0akoQZW29H4QKBgQD1VA0Ja9B5RGzN1TxxZ0gWKPfFYiptewSDVbZZawOycQvzRAl2yOf1ZqGinZNiLM/NcIgbS4f45BCEHvKdbjjLheqt6B50rO+H3KY2Gzy22fCPdl2AKYKJbtaM/YupFHMW859/2RaotLRNHKjmMKztr8cDsWvnF4DLVyfEE4FpfQKBgQDPim2cyONfwMfFArxUBvPO7vQekbIpEiPknGsqH5785Q34QiJB5cKvot/EWRiBCcqtRoFw7mWixZIJ1cbslTC9iz58nrXUrvus0so2Qlw1vbtkkN+WriKwniGkeiyjllFQAqx6yO2U/8XMUayY3SLeX6WXjMBCj9DOuPTzn9QIGQKBgBJm/6NZRwYRr+ewuxv2l12yLQPNXUlhFPGVGFyUvm5ANJlG3T7fyyaJUE6Wa1vN6LiMXGlPvR9aXDLM734HqpL5tKTe6Inv/ve+AWnFmURODNF2G4Vll7kfa1CgJD3Gnut10t0QN7bn9y5hm/nIZPBfYxpP0LxA8xRNsVnv7KExAoGBAMPJ8bJRCb+RXGSNOiGDFYqIfqBADnUUeev3bFmmzxQA+nbT3QGRwWJ0IulWq/k/xYZF12M0Y4l1HVpN5IXYEM0fiWmDSEJGeYMGsGETpW3Fef1bdrLtwCgiZdaGs4n4CumSPCjxWMCRsoYYHt1u8LAcR1l7HP0uM2xxrBQRGbQxAoGAZg+ZQxdrW20DaznJOd0ztH60YXmapW5OY/UmECy44ZModDJ2xiyKoiCgTgWeleLJ26j04T1H71aFTcvq1eeQ2rUs6H+t4e654taIDtGSO5xwbut88XLaGBlbqQQ3zgS/uSV4k+vXWzK6Xl98y+YrTPm8owy27F7LtRz/0LMADG4=";

    //RSA加密算法
    private static String RSA_ALGORITHM;

    @Value("${rsa.rsaAlgorithm}")
    public void setRsaAlgorithm(String rsaAlgorithm) {
        RSAUtil.RSA_ALGORITHM = rsaAlgorithm;
    }

    //签名算法
    private static String SIGNATURE_ALGORITHM;

    @Value("${rsa.signatureAlgorithm}")
    public void setSignatureAlgorithm(String signatureAlgorithm) {
        RSAUtil.SIGNATURE_ALGORITHM = signatureAlgorithm;
    }

    //公钥
    private static String RSA_PUBLIC_KEY;

    @Value("${rsa.publicKey}")
    public void setPublicKey(String publicKey) {
        RSAUtil.RSA_PUBLIC_KEY = publicKey;
    }

    //私钥
    private static String RSA_PRIVATE_KEY;

    @Value("${rsa.privateKey}")
    public void setPrivateKey(String privateKey) {
        RSAUtil.RSA_PRIVATE_KEY = privateKey;
    }

    /**
     * 获取公钥方法
     */
    public static RSAPublicKey getPublicKey() {
        RSAPublicKey publicKey = null;
        try {
            byte[] publicKeyBytes = Base64.getDecoder().decode(RSA_PUBLIC_KEY);
            publicKey = (RSAPublicKey) KeyFactory.getInstance(RSA_ALGORITHM).generatePublic(new X509EncodedKeySpec(publicKeyBytes));
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取公钥失败！");
            throw new BusinessException(HttpResultCodeEnum.GET_RSA_PUBLIC_KEY_ERROR);
        }
        return publicKey;
    }

    /**
     * 获取私钥方法
     */
    public static RSAPrivateKey getPrivateKey() {
        RSAPrivateKey privateKey = null;
        try {
            byte[] privateKeyBytes = Base64.getDecoder().decode(RSA_PRIVATE_KEY);
            privateKey = (RSAPrivateKey) KeyFactory.getInstance(RSA_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取私钥失败！");
            throw new BusinessException(HttpResultCodeEnum.GET_RSA_PRIVATE_KEY_ERROR);
        }
        return privateKey;
    }

    /**
     * RSA加密方法
     *
     * @param text：明文
     * @param key：公钥或私钥
     * @return encrypt：密文
     */
    public static String encrypt(String text, Key key) {
        String encrypt = null;
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] bytes = cipher.doFinal(text.getBytes());
            encrypt = Base64.getEncoder().encodeToString(bytes);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("加密失败！");
            throw new BusinessException(HttpResultCodeEnum.RSA_ENCRYPT_ERROR);
        }
        return encrypt;
    }

    /**
     * RSA解密方法
     *
     * @param text：密文
     * @param key：公钥或私钥
     * @return decrypt：明文
     */
    public static String decrypt(String text, Key key) {
        String decrypt = null;
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, key);
            //byte[] maxResultDecrypt = getMaxResultDecrypt(text, cipher);
            //decrypt = Base64.getEncoder().encodeToString(maxResultDecrypt);
            decrypt = new String(getMaxResultDecrypt(text, cipher));
        } catch (Exception e) {
            e.printStackTrace();
            log.error("解密失败！");
            throw new BusinessException(HttpResultCodeEnum.RSA_DECRYPT_ERROR);
        }
        return decrypt;
    }

    //长度过长分割解密
    private static byte[] getMaxResultDecrypt(String str, Cipher cipher) throws IllegalBlockSizeException, BadPaddingException {
        //byte[] inputArray = cn.hutool.core.codec.Base64.decode(str.getBytes());
        byte[] inputArray = Base64.getDecoder().decode(str.getBytes());
        int inputLength = inputArray.length;
        // 最大解密字节数，超出最大字节数需要分组加密
        int MAX_ENCRYPT_BLOCK = 256;
        // 标识
        int offSet = 0;
        byte[] resultBytes = {};
        byte[] cache = {};
        while (inputLength - offSet > 0) {
            if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(inputArray, offSet, MAX_ENCRYPT_BLOCK);
                offSet += MAX_ENCRYPT_BLOCK;
            } else {
                cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
                offSet = inputLength;
            }
            resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
            System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
        }
        return resultBytes;
    }

//    /**
//     * RSA解密方法
//     *
//     * @param text：密文
//     * @param key：公钥或私钥
//     * @return decrypt：明文
//     */
//    public static String decrypt(String text, Key key) {
//        String decrypt = null;
//        try {
//            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
//            cipher.init(Cipher.DECRYPT_MODE, key);
//            byte[] bytes = cipher.doFinal(text.getBytes());
//            decrypt = Base64.getEncoder().encodeToString(bytes);
//        } catch (Exception e) {
//            e.printStackTrace();
//            log.info("解密失败！");
//            throw new RuntimeException("解密失败！");
//        }
//        return decrypt;
//    }

    /**
     * 使用私钥签名
     *
     * @param text：明文
     * @param privateKey：私钥
     * @return sign：签名
     */
    public static String sign(String text, PrivateKey privateKey) {
        String sign = null;
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initSign(privateKey);
            signature.update(text.getBytes());
            byte[] bytes = signature.sign();
            //sign = Base64.getEncoder().encodeToString(bytes);
            sign = Base64.getEncoder().encodeToString(bytes);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("签名失败！");
            throw new BusinessException(HttpResultCodeEnum.RSA_SIGN_ERROR);
        }
        return sign;
    }

    /**
     * 使用公钥验证签名
     *
     * @param text：明文
     * @param sign：私钥签名
     * @param publicKey：公钥
     * @return verif：验签结果
     */
    public static boolean verif(String text, String sign, PublicKey publicKey) {
        boolean verify = false;
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initVerify(publicKey);
            signature.update(text.getBytes());
            verify = signature.verify(Base64.getDecoder().decode(sign));
        } catch (Exception e) {
            e.printStackTrace();
            log.error("验签失败！");
            throw new BusinessException(HttpResultCodeEnum.RSA_VERIF_SIGN_ERROR);
        }
        return verify;
    }


    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {
        String text = "Hell RSA!";
        log.info("明文：{}", text);

        RSAPublicKey publicKey = getPublicKey();
        log.info("公钥：{}", publicKey);
        RSAPrivateKey privateKey = getPrivateKey();
        log.info("私钥：{}", privateKey);

        //使用公钥加密
        String encryptByPublic = encrypt(text, publicKey);
        log.info("使用公钥加密：{}", encryptByPublic);
        //使用私钥解密
        String decryptByPrivate = decrypt(encryptByPublic, privateKey);
        log.info("使用私钥解密：{}", decryptByPrivate);

        //使用私钥加密
        String encryptByPrivate = encrypt(text, privateKey);
        log.info("使用私钥加密：{}", encryptByPrivate);
        //使用公钥解密
        String decryptByPublic = decrypt(encryptByPrivate, publicKey);
        log.info("使用公钥解密：{}", decryptByPublic);

        //使用私钥签名
        String sign = sign(text, privateKey);
        log.info("使用私钥签名：{}", sign);

        //使用公钥验证签名
        boolean verif = verif(text, sign, publicKey);
        log.info("使用公钥验证签名：{}", verif);
    }

}
